Skip to content

fix(directory): expose directory_group_id filter in list_directory_users#149

Open
ravibits wants to merge 6 commits into
mainfrom
fix/list-directory-users-group-filter
Open

fix(directory): expose directory_group_id filter in list_directory_users#149
ravibits wants to merge 6 commits into
mainfrom
fix/list-directory-users-group-filter

Conversation

@ravibits
Copy link
Copy Markdown
Contributor

@ravibits ravibits commented Apr 28, 2026

Summary

  • list_directory_users was missing the directory_group_id parameter despite it being defined as field 6 in the ListDirectoryUsersRequest proto
  • Added directory_group_id: Optional[str] = None to the method signature and wired it through to the gRPC request
  • Added 3 new test cases: test_list_directory_users, test_list_directory_users_filter_by_group, test_list_directory_groups

Test plan

  • test_list_directory_users — basic call returns OK with no filter
  • test_list_directory_users_filter_by_group — passing a group ID is accepted, returns empty users list for nonexistent group
  • test_list_directory_groups — basic group listing returns OK

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Directory user listing now supports optional filtering by directory group membership.
  • Bug Fixes

    • Directory group listings now reliably return an empty list instead of null when no groups exist.
  • Tests

    • Added tests covering user and group listing, including group-based filtering and basic validation of responses.

The proto field existed (field 6 in ListDirectoryUsersRequest) but was
never wired through the Python SDK wrapper. Adds the missing param and
test coverage for filtering by group and listing groups.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 28, 2026

Review Change Stack

Walkthrough

Adds an optional directory_group_id parameter to DirectoryClient.list_directory_users (documented and forwarded to the RPC), ensures list_directory_groups initializes its groups list, and adds tests validating listing, filtering by group, and group listing.

Changes

Directory client updates and tests

Layer / File(s) Summary
list_directory_users signature & docstring
scalekit/directory.py
Add directory_group_id optional parameter to DirectoryClient.list_directory_users and document the new filter.
list_directory_users request wiring
scalekit/directory.py
Forward directory_group_id into the constructed ListDirectoryUsersRequest sent to ListDirectoryUsers.
list_directory_groups initialization fix
scalekit/directory.py
Set group_response[0].groups = [] before populating returned groups.
Test imports and new tests
tests/test_directory.py
Add imports for SCIM seeding and three tests: test_list_directory_users, test_list_directory_users_filter_by_group, and test_list_directory_groups validating listing and filtering behaviors.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰
I hopped through code with careful paws,
A filter nuzzled in the cause,
Groups now guide which users play,
Tests planted bright to show the way,
A tiny hop, a tidy clause.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: exposing the directory_group_id filter parameter in the list_directory_users method.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/list-directory-users-group-filter

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
tests/test_directory.py (1)

144-162: Strengthen the group-filter test so it actually validates filtering/wiring.

At Lines 157-161, asserting len(response[0].users) == 0 with a nonexistent group can pass even if directory_group_id is ignored (because the directory may already have no users). Consider asserting request wiring via a unit-level mock or using fixture data where unfiltered results are non-empty and filtered results differ.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_directory.py` around lines 144 - 162, The test
test_list_directory_users_filter_by_group currently asserts zero users for a
nonexistent directory_group_id which can pass even if directory_group_id is
ignored; change it to explicitly validate the filter wiring by either (a)
creating fixture data: create at least one user in the directory and a separate
user assigned to a real group, then call list_directory_users twice (once
without directory_group_id and once with a valid directory_group_id) and assert
the unfiltered response contains users while the filtered response only contains
the group member(s), or (b) use a unit-level mock for
scalekit_client.directory.list_directory_users to assert it was called with the
directory_group_id parameter; locate the test by function name
test_list_directory_users_filter_by_group and update calls to
list_directory_users and the setup using CreateDirectory/CreateOrganization to
ensure the scenario proves the filter is applied.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scalekit/directory.py`:
- Around line 75-76: The parameter order change in list_directory_users broke
positional-argument compatibility: move updated_after before directory_group_id
so existing positional callers keep passing their sixth argument to
updated_after; update the function signature of list_directory_users to restore
the original parameter ordering (place updated_after ahead of
directory_group_id) and ensure any internal calls or references to these
parameter names remain working with the restored order.

---

Nitpick comments:
In `@tests/test_directory.py`:
- Around line 144-162: The test test_list_directory_users_filter_by_group
currently asserts zero users for a nonexistent directory_group_id which can pass
even if directory_group_id is ignored; change it to explicitly validate the
filter wiring by either (a) creating fixture data: create at least one user in
the directory and a separate user assigned to a real group, then call
list_directory_users twice (once without directory_group_id and once with a
valid directory_group_id) and assert the unfiltered response contains users
while the filtered response only contains the group member(s), or (b) use a
unit-level mock for scalekit_client.directory.list_directory_users to assert it
was called with the directory_group_id parameter; locate the test by function
name test_list_directory_users_filter_by_group and update calls to
list_directory_users and the setup using CreateDirectory/CreateOrganization to
ensure the scenario proves the filter is applied.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f4d4db48-083b-46e1-9550-5580d7d08797

📥 Commits

Reviewing files that changed from the base of the PR and between 88c7982 and 3f89a47.

📒 Files selected for processing (2)
  • scalekit/directory.py
  • tests/test_directory.py

Comment thread scalekit/directory.py Outdated
ravibits and others added 5 commits April 29, 2026 00:25
…mpty

users attr on ListDirUsersResponse is only set when the loop runs —
accessing it on an empty response raises AttributeError.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…up filter test

- Initialize users=[] and groups=[] before mapping loops in list_directory_users
  and list_directory_groups — slots were unset causing AttributeError on non-empty responses
- Replace weak group filter test (nonexistent ID) with real SCIM-seeded test:
  creates a user+group via SCIM, asserts filtered result contains exactly that user

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ve positional arg order

Inserting before updated_after would silently break callers passing
updated_after positionally.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
tests/test_directory.py (3)

159-159: 💤 Low value

Direct os.environ[...] access yields an opaque KeyError when unset.

If SCALEKIT_ENV_URL isn't configured, this fails with a bare KeyError rather than a clear skip/assertion. Consider guarding it.

♻️ Optional guard
-        env_url = os.environ['SCALEKIT_ENV_URL'].rstrip('/')
+        env_url = os.environ.get('SCALEKIT_ENV_URL')
+        if not env_url:
+            self.skipTest("SCALEKIT_ENV_URL not configured")
+        env_url = env_url.rstrip('/')
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_directory.py` at line 159, The test currently reads
SCALEKIT_ENV_URL directly into env_url which raises a bare KeyError if the env
var is missing; change the access in tests/test_directory.py so you first
retrieve the value with os.environ.get or os.getenv('SCALEKIT_ENV_URL') and, if
it's None/empty, call pytest.skip (or assert with a clear message) before using
.rstrip('/'); update the env_url assignment and any subsequent use to handle the
guarded case and reference SCALEKIT_ENV_URL and the env_url variable so the test
fails/skips with a clear message instead of raising KeyError.

173-189: ⚡ Quick win

Add a timeout to the SCIM requests.post calls.

Both POSTs lack a timeout, so a stalled SCIM endpoint can hang the test run indefinitely. Flagged by static analysis (S113).

♻️ Add timeouts
         user_r = requests.post(f'{scim_base}/Users', json={
             'schemas': ['urn:ietf:params:scim:schemas:core:2.0:User'],
             'userName': email,
             'name': {'givenName': fake.first_name(), 'familyName': fake.last_name()},
             'emails': [{'value': email, 'primary': True}],
             'active': True
-        }, headers=headers)
+        }, headers=headers, timeout=30)
         group_r = requests.post(f'{scim_base}/Groups', json={
             'schemas': ['urn:ietf:params:scim:schemas:core:2.0:Group'],
             'displayName': fake.bs(),
             'members': [{'value': scim_user_id}]
-        }, headers=headers)
+        }, headers=headers, timeout=30)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_directory.py` around lines 173 - 189, The two SCIM POST calls
creating user_r and group_r currently call requests.post without a timeout;
update both calls (the requests.post that assigns user_r and the requests.post
that assigns group_r) to pass an explicit timeout (e.g.
timeout=<reasonable_seconds> or a TEST_TIMEOUT constant) so the tests fail fast
on a stalled SCIM endpoint; ensure both requests include the same timeout value
and run the tests to confirm no breakage.

199-200: 🏗️ Heavy lift

Remove the SCIM-id vs directory-id mismatch concern; keep an eventual-consistency/flakiness guard

  • list_directory_users populates response[0].users[].id by copying the underlying ListDirectoryUsers user.id directly (dir_user.id = user.id), and the protobuf documents DirectoryUser.id as Scalekit’s “User ID” (diruser_*). Since the test uses the SCIM id both for group members[].value and for the final users[0].id assertion, the identifier types are aligned with what the directory API returns.
  • The only remaining risk is timing: if the SCIM POSTs aren’t immediately reflected in list_directory_users, lines 199-200 may become flaky; add polling/retry (or an explicit sync trigger) if failures occur.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_directory.py` around lines 199 - 200, The test's assertion
comparing response[0].users[0].id to scim_user_id is valid (IDs align), but may
be flaky due to eventual consistency; update the test around
list_directory_users to retry/poll until the expected user appears (e.g., call
list_directory_users in a short loop with sleeps and a timeout) instead of a
single immediate assert, so functions/methods involved are list_directory_users
and the test's scim POST flow that creates the user; keep the same final
equality assertion but gate it behind the retry/poll loop to avoid transient
failures.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@tests/test_directory.py`:
- Line 159: The test currently reads SCALEKIT_ENV_URL directly into env_url
which raises a bare KeyError if the env var is missing; change the access in
tests/test_directory.py so you first retrieve the value with os.environ.get or
os.getenv('SCALEKIT_ENV_URL') and, if it's None/empty, call pytest.skip (or
assert with a clear message) before using .rstrip('/'); update the env_url
assignment and any subsequent use to handle the guarded case and reference
SCALEKIT_ENV_URL and the env_url variable so the test fails/skips with a clear
message instead of raising KeyError.
- Around line 173-189: The two SCIM POST calls creating user_r and group_r
currently call requests.post without a timeout; update both calls (the
requests.post that assigns user_r and the requests.post that assigns group_r) to
pass an explicit timeout (e.g. timeout=<reasonable_seconds> or a TEST_TIMEOUT
constant) so the tests fail fast on a stalled SCIM endpoint; ensure both
requests include the same timeout value and run the tests to confirm no
breakage.
- Around line 199-200: The test's assertion comparing response[0].users[0].id to
scim_user_id is valid (IDs align), but may be flaky due to eventual consistency;
update the test around list_directory_users to retry/poll until the expected
user appears (e.g., call list_directory_users in a short loop with sleeps and a
timeout) instead of a single immediate assert, so functions/methods involved are
list_directory_users and the test's scim POST flow that creates the user; keep
the same final equality assertion but gate it behind the retry/poll loop to
avoid transient failures.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 926d0604-85aa-4c62-8703-41da6cf5a0fd

📥 Commits

Reviewing files that changed from the base of the PR and between 3f89a47 and d396230.

📒 Files selected for processing (2)
  • scalekit/directory.py
  • tests/test_directory.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • scalekit/directory.py

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants